package spring.security.certs;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.*;
import java.security.cert.Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

public class RSACoder {
    //非对称密钥算法
    public static final String KEY_ALGORITHM="RSA";
    /**
     * 密钥长度，DH算法的默认密钥长度是1024
     * 密钥长度必须是64的倍数，在512到65536位之间
     * */
    private static final int KEY_SIZE=512;
    //公钥
    public static final String PUBLIC_KEY="public_key";

    //私钥
    public static final String PRIVATE_KEY="private_key";

    /**
     * 初始化密钥对
     * @return Map 甲方密钥的Map
     * */
    public static Map<String,Object> initKey() throws Exception{
        //实例化密钥生成器
        KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance(KEY_ALGORITHM);
        //初始化密钥生成器
        keyPairGenerator.initialize(KEY_SIZE);
        //生成密钥对
        KeyPair keyPair=keyPairGenerator.generateKeyPair();
        //甲方公钥
        RSAPublicKey publicKey=(RSAPublicKey) keyPair.getPublic();
        System.out.println("系数："+publicKey.getModulus()+"  加密指数："+publicKey.getPublicExponent());
        //甲方私钥
        RSAPrivateKey privateKey=(RSAPrivateKey) keyPair.getPrivate();
        System.out.println("系数："+privateKey.getModulus()+"解密指数："+privateKey.getPrivateExponent());
        //将密钥存储在map中
        Map<String,Object> keyMap=new HashMap<String,Object>();
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }


    public static Map<String,Object> initKey(InputStream fpxStream,String strPassword) throws Exception{
        KeyStore ks = KeyStore.getInstance("PKCS12");
        // If the keystore password is empty(""), then we have to set
        // to null, otherwise it won't work!!!
        char[] nPassword = null;
        if ((strPassword == null) || strPassword.trim().equals("")) {
            nPassword = null;
        } else {
            nPassword = strPassword.toCharArray();
        }
        ks.load(fpxStream, nPassword);
        fpxStream.close();
        System.out.println("keystore type=" + ks.getType());
        // Now we loop all the aliases, we need the alias to get keys.
        // It seems that this value is the "Friendly name" field in the
        // detals tab <-- Certificate window <-- view <-- Certificate
        // Button <-- Content tab <-- Internet Options <-- Tools menu
        // In MS IE 6.
        Enumeration enumas = ks.aliases();
        String keyAlias = null;
        if (enumas.hasMoreElements())// we are readin just one certificate.
        {
            keyAlias = (String) enumas.nextElement();
            System.out.println("alias=[" + keyAlias + "]");
        }

        System.out.println();
        System.out.println("is key entry=" + ks.isKeyEntry(keyAlias));
        PrivateKey prikey = (PrivateKey) ks.getKey(keyAlias, nPassword);
        Certificate cert = ks.getCertificate(keyAlias);
        PublicKey pubkey = cert.getPublicKey();


       //将密钥存储在map中
        Map<String,Object> keyMap=new HashMap<String,Object>();
        keyMap.put(PUBLIC_KEY, pubkey);
        keyMap.put(PRIVATE_KEY, prikey);
        return keyMap;
    }


    /**
     * 私钥加密
     * @param data待加密数据
     * @param key 密钥
     * @return byte[] 加密数据
     * */
    public static byte[] encryptByPrivateKey(byte[] data,byte[] key) throws Exception{

        //取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(key);
        KeyFactory keyFactory= KeyFactory.getInstance(KEY_ALGORITHM);
        //生成私钥
        PrivateKey privateKey=keyFactory.generatePrivate(pkcs8KeySpec);
        //数据加密
        Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 公钥加密
     * @param data待加密数据
     * @param key 密钥
     * @return byte[] 加密数据
     * */
    public static byte[] encryptByPublicKey(byte[] data,byte[] key) throws Exception{

        //实例化密钥工厂
        KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公钥
        //密钥材料转换
        X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(key);
        //产生公钥
        PublicKey pubKey=keyFactory.generatePublic(x509KeySpec);

        //数据加密
        Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        return cipher.doFinal(data);
    }
    /**
     * 私钥解密
     * @param data 待解密数据
     * @param key 密钥
     * @return byte[] 解密数据
     * */
    public static byte[] decryptByPrivateKey(byte[] data,byte[] key) throws Exception{
        //取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(key);
        KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
        //生成私钥
        PrivateKey privateKey=keyFactory.generatePrivate(pkcs8KeySpec);
        //数据解密
        Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }



    /**
     * 签名
     *
     * @return 签名后经过base64处理的字符串
     * @throws Exception
     */
    public static String signByPrivateKey(String str,byte[] key) {
        String base64Sign = "";
        try {
            //取得私钥
            PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(key);
            KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
            //生成私钥
            PrivateKey privateKey=keyFactory.generatePrivate(pkcs8KeySpec);
            // 签名
            Signature sign = Signature.getInstance("SHA1withRSA");
            sign.initSign(privateKey);
            byte[] bysData = str.getBytes("UTF-8");
            sign.update(bysData);
            byte[] signByte = sign.sign();
            BASE64Encoder encoder = new BASE64Encoder();
            base64Sign = encoder.encode(signByte);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return base64Sign;
    }

    /**
     * 数据验证
     *
     * @param signStr 加密后的数据
     * @param verStr  原始字符
     * @return
     */
    public static boolean verifyByPublicKey(String signStr, String verStr,byte[] key) throws Exception{
       //实例化密钥工厂
        KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公钥
        //密钥材料转换
        X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(key);
        //产生公钥
        PublicKey pubKey=keyFactory.generatePublic(x509KeySpec);

        BASE64Decoder decoder = new BASE64Decoder();
        byte[] signed = decoder.decodeBuffer(signStr);

        Signature sign = Signature.getInstance("SHA1withRSA");
        sign.initVerify(pubKey);
        sign.update(verStr.getBytes("UTF-8"));
        boolean verfy = false;
        verfy = sign.verify(signed);
        return verfy;
    }

    /**
     * 公钥解密
     * @param data 待解密数据
     * @param key 密钥
     * @return byte[] 解密数据
     * */
    public static byte[] decryptByPublicKey(byte[] data,byte[] key) throws Exception{

        //实例化密钥工厂
        KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公钥
        //密钥材料转换
        X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(key);
        //产生公钥
        PublicKey pubKey=keyFactory.generatePublic(x509KeySpec);
        //数据解密
        Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, pubKey);
        return cipher.doFinal(data);
    }

    /**
     * 取得私钥
     * @param keyMap 密钥map
     * @return byte[] 私钥
     * */
    public static byte[] getPrivateKey(Map<String,Object> keyMap){
        Key key=(Key)keyMap.get(PRIVATE_KEY);
        return key.getEncoded();
    }
    /**
     * 取得公钥
     * @param keyMap 密钥map
     * @return byte[] 公钥
     * */
    public static byte[] getPublicKey(Map<String,Object> keyMap) throws Exception{
        Key key=(Key) keyMap.get(PUBLIC_KEY);
        return key.getEncoded();
    }
    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        //初始化密钥
        //生成密钥对
//        Map<String,Object> keyMap=RSACoder.initKey();

//        String strPfx = "C:\\Users\\wangj\\Desktop\\certs\\child_test.pfx";
        String strPassword = "jun195273";

        URL url = new URL("http://106.54.225.249:9800/files/child_test.pfx");
        HttpURLConnection connection = (HttpURLConnection)url.openConnection();
        //设置超时间为3秒
        connection.setConnectTimeout(30*1000);
        connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

        Map<String,Object> keyMap=RSACoder.initKey(connection.getInputStream(),strPassword);

        //公钥
        byte[] publicKey=RSACoder.getPublicKey(keyMap);
        //byte[] publicKey = b;
        //私钥
        byte[] privateKey=RSACoder.getPrivateKey(keyMap);
        System.out.println("公钥："+ Base64.encode(publicKey));
        System.out.println("私钥："+Base64.encode(privateKey));

        System.out.println("================密钥对构造完毕,甲方将公钥公布给乙方，开始进行加密数据的传输=============");
        String str="aattaggcctegthththfef/aat.mp4";
        System.out.println("===========甲方向乙方发送加密数据==============");
        System.out.println("原文:"+str);
        //甲方进行数据的加密
        byte[] code1=RSACoder.encryptByPublicKey(str.getBytes(), publicKey);
        System.out.println("甲方 使用乙方公钥加密后的数据："+Base64.encode(code1));
        System.out.println("===========乙方使用甲方提供的公钥对数据进行解密==============");
        //乙方进行数据的解密
        //byte[] decode1=RSACoder.decryptByPublicKey(code1, publicKey);
        byte[] decode1=RSACoder.decryptByPrivateKey(code1, privateKey);
        System.out.println("乙方解密后的数据："+new String(decode1)+"");

        System.out.println("===========反向进行操作，签名验证==============");

        str="乙方向甲方发送数据RSA算法";

        System.out.println("原文:"+str);

        //乙方使用公钥对数据进行加密
        String source=RSACoder.signByPrivateKey(str, privateKey);
        System.out.println("===========乙方使用公钥对数据进行签名==============");
        System.out.println("加密后的数据："+source);

        System.out.println("=============乙方将数据传送给甲方======================");
        System.out.println("===========甲方使用公钥对数据验证签名==============");

        //甲方使用公钥验证签名
        boolean check=RSACoder.verifyByPublicKey(source,str,publicKey);

        System.out.println("甲方使用公钥对数据验证签名的结果是："+check);


        str="乙方向甲方发送数据RSA算法";
        System.out.println("原文:"+str);
        byte[] code3=RSACoder.encryptByPrivateKey(str.getBytes(), privateKey);
        System.out.println("乙方私钥加密后的数据："+Base64.encode(code1));

        byte[] code4=RSACoder.decryptByPublicKey(code3,publicKey);
        System.out.println("甲方公钥解密后的数据："+new String(code4)+"");

    }
}
